﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MethodWorx.CMS.Providers;
using System.IO;
using Microsoft.Win32;
using MethodWorx.Core;

namespace MethodWorx.CMS.Plugins.AssetManagement
{
    public class AssetManagementService
    {
        private PluginService service;
        private IPlugin plugin;
        //  data caches
        private AssetFolder root;
        private Dictionary<AssetFolder, AssetMap> maps = new Dictionary<AssetFolder, AssetMap>();

        public AssetManagementService(ICmsDataProvider provider)
        {
            this.service = new PluginService(provider);
            var repository =  new PluginRepository(provider);
            this.plugin = repository.All().First(p => p.Id == AssetManagementExtension.GetPluginId());
        }


        public AssetFolder GetAssetFolder(Guid instance)
        {
            var assetFolder = this.GetRootAssetFolder();
            if (assetFolder == null)
                throw new Exception("Asset folder does not exist");

            assetFolder = assetFolder.Find(instance);
            if (assetFolder == null)
                throw new Exception("Asset folder does not exist");

            return assetFolder;
        }

        public AssetFolder GetRootAssetFolder()
        {
            if (this.root == null)
            {
                var assetFolder = this.service.GetUserData<AssetFolder>(this.plugin, null, "AssetFolder");
                if (assetFolder == null)
                {
                    assetFolder = new AssetFolder();
                    assetFolder.Name = "ROOT";
                    assetFolder.TemplateAssets = false;
                    assetFolder.Id = Guid.Empty;

                    //
                    //  set the user data
                    this.service.SetUserData(this.plugin, null, "AssetFolder", assetFolder, true);
                }
                this.root = assetFolder;
            }


            return this.root;
        }

        public IEnumerable<PluginUserData<Asset>> GetAssets(Guid guid)
        {
            var folder = this.GetRootAssetFolder();
            if (guid != Guid.Empty)
                folder = this.GetAssetFolder(guid);

            //
            //  ok, here we go
            //  lets enumerate the assets
            foreach (var pud in this.service.GetAllUserData<Asset>(this.plugin, folder, "Asset"))
            {
                yield return pud;
            }
        }

        public AssetFolder GetAssetFolder(string path)
        {
            var root = this.GetRootAssetFolder();
            var parts = path.Split(new string [] { "/" }, StringSplitOptions.RemoveEmptyEntries);
            var index = 0;

            if(parts.Length == 0)
            {
                //
                //  ok, one part
                return root;
            }
            else
            {
                var finished = false;

                while(!finished)
                {
                    bool found = false;
                    foreach(var child in root.Children ?? new AssetFolder [0])
                    {
                        if (child.Name.ToLower() == parts[index].ToLower())
                        {
                            index++;
                            root = child;
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                        break;
                    else
                    {
                        if (index == parts.Length - 1)
                            return root;
                    }
                }
                return null;
            }
        }

        public  void RenameAssetFolder(Guid instance, string newName)
        {
            var rootAssetFolder = this.GetRootAssetFolder();
            if (rootAssetFolder == null)
                throw new Exception("Asset folder does not exist");

            var assetFolder = rootAssetFolder.Find(instance);
            if (assetFolder == null)
                throw new Exception("Asset folder does not exist");

            assetFolder.Name = newName;

            this.service.SetUserData(this.plugin, null, "AssetFolder", rootAssetFolder, true);
        }

        public void MoveAssets(Guid from, Guid[] selected, Guid to)
        {
            var rootAssetFolder = this.GetRootAssetFolder();
            var fromFolder = rootAssetFolder.Find(from);
            var toFolder = rootAssetFolder.Find(to);

            //
            //  ok,everything exists
            if (fromFolder != null && toFolder != null)
            {
                //
                //  ok, here we go
                //  lets enumerate the assets
                var fromMap = this.GetAssetMap(fromFolder);
                var toMap = this.GetAssetMap(toFolder);

                foreach (var pud in this.service.GetAllUserData<Asset>(this.plugin, fromFolder, "Asset"))
                {
                    
                    if (selected.Contains(pud.Id))
                    {
                        fromMap.RemoveByKey(pud.Id);
                        //  the id is here
                        //  we want to delete it
                        this.service.DeleteUserData(pud.Id);
                        var newPud = this.service.SetUserData(this.plugin, toFolder, "Asset", pud.UserData, false);

                        toMap.Map.Add(pud.UserData.Name.ToLower(), newPud.Id);
                    }
                }

                this.service.SetUserData(this.plugin, toFolder, "AssetMap", fromMap, true);
                this.service.SetUserData(this.plugin, toFolder, "AssetMap", toMap, true);
            }
            this.service.SetUserData(this.plugin, null, "AssetFolder", rootAssetFolder, true);
        }

        public string GetPath(AssetFolder folder)
        {
            return this.GetPath(folder, true);
        }

        public string GetPath(AssetFolder folder, bool withRoot)
        {
            StringBuilder sb = new StringBuilder();
            while (folder != null)
            {
                if (folder.Id == Guid.Empty)
                {
                    if(withRoot)
                        sb.Insert(0, "/" + folder.Name);
                }
                else
                    sb.Insert(0, "/" + folder.Name);
                     

                folder = this.GetParent(folder);
            }
            return sb.ToString();
        }

        public AssetFolder GetParent(AssetFolder folder)
        {
            var assetFolder = this.GetRootAssetFolder();
            if (assetFolder == null)
                throw new Exception("Asset folder does not exist");

            AssetFolder parent = null;
            assetFolder.FindWithParent(folder.Id, out parent);
            if (assetFolder == null)
                throw new Exception("Asset folder does not exist");

            return parent;
        }

        public AssetFolder CreateAssetFolder(Guid? assetFolderId, string name, bool templateAssets)
        {
            var assetFolder = this.GetRootAssetFolder();

            var created = assetFolder.Find(assetFolderId).CreateAssetFolder(name, templateAssets);
            //
            //  set the user data
            this.service.SetUserData(this.plugin, null, "AssetFolder", assetFolder, true);
            return created;
        }


        public void CreateAsset(AssetFolder folder, string name, string mimeType, byte[] data)
        {
            var assetFolder = this.GetAssetFolder(folder.Id);

            Asset asset = new Asset();
            asset.Name = name;
            asset.MimeType = mimeType;
            asset.Data = data;

            //  and save
            var pud = this.service.SetUserData(
                this.plugin,
                assetFolder,
                "Asset",
                asset,
                false);

            var map = GetAssetMap(assetFolder);
            map.Map.Add(asset.Name.ToLower(), pud.Id);
            this.service.SetUserData(this.plugin, assetFolder, "AssetMap", map, true);
        }

        private AssetMap GetAssetMap(AssetFolder assetFolder)
        {
            if (this.maps.ContainsKey(assetFolder))
                return this.maps[assetFolder];
            else
            {
                var map = this.service.GetUserData<AssetMap>(this.plugin, assetFolder, "AssetMap");
                if (map == null)
                {
                    map = new AssetMap();
                    map.Map = new Dictionary<string, Guid>();
                }
                this.maps.Add(assetFolder, map);
            }

            return this.maps[assetFolder];
        }

        public string GetMimeType(string filename)
        {
            
            var extension = Path.GetExtension(filename);
            var mimeType = MimeTypes.GetMimeTypeFromExtension(extension, "application/unknown");

            return mimeType;
        }

        public void DeleteAssetFolder(Guid guid)
        {
            AssetFolder parent;
            var folder = this.GetRootAssetFolder().FindWithParent(guid, out parent);
            if(folder != null && parent != null)
            {                
                var assets = this.GetAssets(folder.Id);

                foreach (var asset in assets)
                {
                    service.DeleteUserData(asset.Id);
                }
                //  delete the asset map for the folder
                this.service.DeleteSingleUserData(this.plugin, folder, "AssetMap");

                parent.Children = parent.Children.Where(c => c != folder).ToArray();
                this.service.SetUserData(this.plugin, null, "AssetFolder", this.GetRootAssetFolder(), true);
            }
        }
    }
}
